package com.yamidragut.simpleebooksreader;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import nl.siegmann.epublib.domain.Book;
import nl.siegmann.epublib.epub.EpubReader;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import com.dropbox.sync.android.DbxAccountManager;
import com.dropbox.sync.android.DbxException;
import com.dropbox.sync.android.DbxException.Unauthorized;
import com.dropbox.sync.android.DbxFile;
import com.dropbox.sync.android.DbxFileInfo;
import com.dropbox.sync.android.DbxFileSystem;
import com.dropbox.sync.android.DbxFileSystem.PathListener;
import com.dropbox.sync.android.DbxPath;

public class EbookListActivity extends ActionBarActivity implements PathListener{
	
    private TextView mLinkExplanation;
    private Button mLinkButton;
    private DbxAccountManager mDbxAcctMgr;
    private DbxFileSystem dbxFs;
    
    private ListView ebookList;
    private ProgressBar progressBar;
    
    private LoadCoverTask loadCoverTask = null;
    private int currentSelectedPosition = -1;
    
    private CustomListAdapter adapter;
    private ArrayList<DbxFileInfo> filteredEbookInfos; // only epub
    
    private Util.CURRENT_SORT_MODE currentSortMode;
    
    public static final String LOG = "EbookListActivity";


	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_ebook_list);
		
		mLinkExplanation = (TextView) findViewById(R.id.link_dropbox_explanation);
		mLinkButton = (Button) findViewById(R.id.link_button);
		ebookList = (ListView) findViewById(R.id.ebook_list_view);
		progressBar = (ProgressBar) findViewById(R.id.progress_bar_2);
		
		currentSortMode = Util.CURRENT_SORT_MODE.NOT_SORTED;
		
		filteredEbookInfos = new ArrayList<DbxFileInfo>();
		
		// Adapter that manages the listView changes
		adapter = new CustomListAdapter(this, R.layout.custom_row_list, filteredEbookInfos);
		ebookList.setAdapter(adapter);
		
		// Get the account manager
		mDbxAcctMgr = DbxAccountManager.getInstance(getApplicationContext(), Util.appKey, Util.appSecret);
		
		if (mDbxAcctMgr.hasLinkedAccount()) {
		    showLinkedView();
		    getEbookList();
		} else {
			showUnlinkedView();
		}

	}
	
	
	@Override
	protected void onResume() {
		super.onResume();
		
		// Only adds the listener if the list has been initialized not at the opening of the app
		if (dbxFs != null) {
			dbxFs.addPathListener(this, DbxPath.ROOT, PathListener.Mode.PATH_OR_DESCENDANT);
		}
	}
	
	
	@Override
	protected void onPause() {
		super.onPause();
		
		// Only remove the listener if the list has been initialized
		if (dbxFs != null) {
			dbxFs.removePathListener(this, DbxPath.ROOT, PathListener.Mode.PATH_OR_DESCENDANT);
		}
	}
	
	
	@Override
	protected void onDestroy() {
		// Delete all bitmaps in the temporally directory
		new FilesSaver(this).deleteBookCoverDirectory(Util.COVER_DIRECTORY);
		
		super.onDestroy();
	}
	
	
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.ebook_list, menu);
		
		return true;
	}
		
	
	// When the account is linked the ebook list is showed
    private void showLinkedView() {
    	mLinkExplanation.setVisibility(View.GONE);
        mLinkButton.setVisibility(View.GONE);
        progressBar.setVisibility(View.VISIBLE);
        ebookList.setVisibility(View.GONE);
    }

    
    // When the account is unlinked the link explanation and button are showed 
    private void showUnlinkedView() {
    	mLinkExplanation.setVisibility(View.VISIBLE);
        mLinkButton.setVisibility(View.VISIBLE);
        progressBar.setVisibility(View.GONE);
        ebookList.setVisibility(View.GONE);      
    }
	
    
    // Callback method for the onClick listener of the link button put in the layout XML file
    // The api of dropbox let the user enter his/her credentials of Dropbox, and he/she has to accept
    // the use of their dropbox data
    // The app only have file pemission, so it can only retrieve eBook data from the Dropbox account
    public void onClickLinkToDropbox(View v) {
        mDbxAcctMgr.startLink((Activity)this, Util.REQUEST_LINK_TO_DBX);
    }
    
    
    // Callback method for the menu item in order to unlink the current Dropbox account
    public void onUnlinkFromDropbox(MenuItem item) {
    	setUnlinkAdvice();
    }
    
    
    // Check the result from the linking to Dropbox
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
    	
        if (requestCode == Util.REQUEST_LINK_TO_DBX) {
            if (resultCode == Activity.RESULT_OK) {
            	showLinkedView();
            	getEbookList();
            } else {
            	setAcceptErrorDialog(getString(R.string.link_error));
            }
        } else {
            super.onActivityResult(requestCode, resultCode, data);
        }
    }
    
    
    // Search for the books and add them to the list
    private void getEbookList() {
    	
        try {
        	
			// Create DbxFileSystem for synchronized file access.
			dbxFs = DbxFileSystem.forAccount(mDbxAcctMgr.getLinkedAccount());
			
			dbxFs.addPathListener(this, DbxPath.ROOT, PathListener.Mode.PATH_OR_DESCENDANT);

			lookThroughFolderForEpub(DbxPath.ROOT);
			
			progressBar.setVisibility(View.GONE);
			ebookList.setVisibility(View.VISIBLE);
			
		} catch (Unauthorized e) {
			Log.e(LOG, "Unauthorized Exception in getEbookList: " +  e);
		}

    }
    
    
    // Looks for the .epub books in all the folders. List the info of all the files of the current folder
    // If a file is a folder enters in that folder in order to continue searching for epub files, so it searches 
    // recursively in all folders 
    private void lookThroughFolderForEpub (DbxPath path) {
    	
    	try {
    		
			List<DbxFileInfo> ebooksInfos = dbxFs.listFolder(path);
			
			for (DbxFileInfo info : ebooksInfos) {
				
				if (info.isFolder) {					
					lookThroughFolderForEpub (info.path);
					
				} else {
					
					Log.d(LOG, "path ebook: " + info.path.getName());
					
	            	// Add only files that ends with ".epub"
	            	if (info.path.toString().endsWith(Util.EPUB_FILE)) {
	            		filteredEbookInfos.add(info);
	            		
	            		if (adapter != null) {
	            			// Notify to the adapter that the data have changed
	                		adapter.notifyDataSetChanged();
	            		}
					}
				}

			}
			
		} catch (DbxException e) {
			Log.e(LOG, "DbxException Exception in lookThroughFolderForEpub: " +  e);
		}
    	
    }
    
    
    // Callback method for the menu sorting options
    public void onSort(MenuItem item) {
    	
    	if(item.getItemId() ==  R.id.order_by_title && currentSortMode != Util.CURRENT_SORT_MODE.BY_TITLE) {   		
    		Collections.sort(filteredEbookInfos, Util.SORT_BY_TITLE);	        	
    	} else if (item.getItemId() ==  R.id.order_by_date && currentSortMode != Util.CURRENT_SORT_MODE.BY_DATE) {
    		Collections.sort(filteredEbookInfos, Util.SORT_BY_DATE);
    	} else {
    		return;
    	}
    	
		if (adapter != null) {
			// Notify to the adapter that the data have changed
    		adapter.notifyDataSetChanged();
		}
    	
    }
    
    
    // onDoubleTap in an icon in order to show the cover
    public void onCoverDoubleTap(int position) {
        
    	currentSelectedPosition = position;
		DbxFile testFile = null;
		
		String storedCoverPath = getPathIfCoverIsStored();
		
		// Check first if the cover is already stored in the temporally directory
		if (storedCoverPath != null) {
			Log.d(LOG, "Already cover stored!");
            Intent i = new Intent(this,  ShowCoverActivity.class);
			i.putExtra(Util.COVER_PATH_EXTRA, storedCoverPath);
            startActivity(i);
		
		} else {
			
			progressBar.setVisibility(View.VISIBLE);
		
			try {
				// These lines of code can't go in another thread because the Sync Api has to be accessed from
				// the same thread always
				testFile = dbxFs.open(filteredEbookInfos.get(currentSelectedPosition).path);
				FileInputStream epubInputStream = testFile.getReadStream();
				
				// Load Book from inputStream		
			    Book book = (new EpubReader()).readEpub(epubInputStream);
			    
				loadCoverTask = new LoadCoverTask();
				loadCoverTask.execute(book);
				
			} catch (DbxException e) {
				Log.e(LOG, "DbxException in getCoverBitmap: " + e);
	        	
	        } catch (IOException e) {
	        	Log.e(LOG, "IOException in getCoverBitmap: " + e);
	        	
	        } catch (Exception e) {
	        	Log.e(LOG, "Exception in getCoverBitmap: " + e);
				
			} finally {
	        	if (testFile != null) {
	        		testFile.close();
	        	}
	        }
		
		}
        
    }
         
    
    // show simple error dialog to inform the user
    private void setAcceptErrorDialog(String errorText) {
    	AlertDialog.Builder errorDialog = new AlertDialog.Builder(this);
    	errorDialog.setMessage(errorText);
   
    	// Positive button
    	errorDialog.setPositiveButton(getString(android.R.string.ok), new DialogInterface.OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which) {
				// do nothing, still showing the link button
			}
		});
    	
    	errorDialog.show();
    	
    }

    
    // show the confirmation dialog for the unlink
	public void setUnlinkAdvice() {

		AlertDialog.Builder alertDialog = new AlertDialog.Builder(this);
		alertDialog.setTitle(R.string.unlink_dropbox);
		alertDialog.setMessage(getString(R.string.unlink_dropbox_advice));

		alertDialog.setPositiveButton(getString(android.R.string.yes), new DialogInterface.OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which) {
				mDbxAcctMgr.unlink();
				showUnlinkedView();
			}
		});

		alertDialog.setNegativeButton(getString(android.R.string.cancel), new DialogInterface.OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which) {
				//Nothing to do here
			}
		});	

		alertDialog.setCancelable(true);
		alertDialog.show();
	}
    

    // Listener for changes in the dropbox data in order to get updated the list
	@Override
	public void onPathChange(DbxFileSystem arg0, DbxPath arg1, Mode arg2) {
		
		// Reset the list in order not to get duplicates
		filteredEbookInfos.clear();
		if (adapter != null) {
			// Notify to the adapter that the data have changed
    		adapter.notifyDataSetChanged();
		}
		
		getEbookList();
	}
	
	
	/**
	 * Returns the absolute path of the bitmap of the cover if the image is stored in the temporally directory,
	 * returns null if not 
	 * @return
	 */
	private String getPathIfCoverIsStored() {
		String fileName = Util.stripExtension(Util.EPUB_FILE, filteredEbookInfos.get(currentSelectedPosition).path.getName()) 
				+ Util.COVER_FILE_EXTENSION;
		
		String coverPath = null;
		coverPath = new FilesSaver(getApplicationContext()).getPathIfAlreadyStored(Util.COVER_DIRECTORY, fileName);
		
		return coverPath;
	}
	
	
	/**
	 * This class is an asynchronous task used to decode the bitmap of the cover of the book 
	 *
	 */
	public class LoadCoverTask extends AsyncTask<Book, Void, String> {

		@Override
		protected String doInBackground(Book... book) {
			
			Bitmap cover = null;
			String coverPath = null;
			    
			try {
			    // Log the book's coverimage property
			    cover = BitmapFactory.decodeStream(book[0].getCoverImage().getInputStream());
			    
			} catch (IOException e) {
				Log.e(LOG, "IOException in doInBackground: " + e);
			} catch (Exception e) {
				Log.e(LOG, "Exception in doInBackground: " + e);
			}
			
			if (cover != null) {
				FilesSaver fileSaver = new FilesSaver(EbookListActivity.this.getApplicationContext());
				String fileName = Util.stripExtension(Util.EPUB_FILE, EbookListActivity.this.filteredEbookInfos.get(EbookListActivity.this.currentSelectedPosition).path.getName()) 
						+ Util.COVER_FILE_EXTENSION;				
				
				coverPath = fileSaver.saveBitmapToInternalSorage(cover, Util.COVER_DIRECTORY, fileName);
			}
			
			return coverPath;
		}


		@Override
		protected void onPostExecute(final String coverPath) {
			loadCoverTask = null;

	        if (coverPath != null) {	
	        	// Put the absolute path as an extra in order to pass it to the ShowCoverActivity
	            Intent i = new Intent(EbookListActivity.this,  ShowCoverActivity.class);
				i.putExtra(Util.COVER_PATH_EXTRA, coverPath);
	            EbookListActivity.this.startActivity(i);
				
	        } else {
	        	Toast.makeText(EbookListActivity.this, getString(R.string.no_cover_available), Toast.LENGTH_LONG).show();
	        }

			progressBar.setVisibility(View.GONE);		

		}

		@Override
		protected void onCancelled() {
			loadCoverTask = null;
			Toast.makeText(EbookListActivity.this, getString(R.string.get_cover_task_canceled), Toast.LENGTH_LONG).show();
			progressBar.setVisibility(View.GONE);
		}

	}


}
